package framework.beans;

import framework.common.XConfig;
import framework.handler.XRequestHandler;
import framework.registry.Registry;
import framework.registry.ZookeeperRegistry;
import framework.transport.client.ChannelProvider;
import framework.transport.client.NettyClient;
import framework.transport.cluster.Cluster;
import framework.transport.cluster.FailFastCluster;
import framework.transport.cluster.FailOverCluster;
import framework.transport.loadbalance.LoadBalance;
import framework.transport.loadbalance.RandomLoadBalance;
import framework.transport.server.NettyServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Configuration
public class XRpcAutoConfiguration {


    private final String zookeeper = "zookeeper";
    private final String nacos = "nacos";

    @Bean
    public Registry registry(XConfig config) {

        Registry registry = null;
        String address = config.getRegister().getAddress();
        Map<String, String> map;
        try {
            map = getProtocolDetails(address);
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
        String protocol = map.get("protocol");
        String addr = map.get("address");
        switch (protocol) {
            case zookeeper:
                log.debug("{}",zookeeper);
                registry = new ZookeeperRegistry(addr);
                break;
            case nacos:
                log.debug("{}",nacos);
                break;
        }
        if (registry == null) {
            throw new RuntimeException("registry is null");
        }
        return registry;
    }

    private HashMap<String, String> getProtocolDetails(String url) throws URISyntaxException {

        URI u = new URI(url);
        String protocol = u.getScheme();
        String address = u.getAuthority();
        HashMap<String, String> map = new HashMap<>();
        map.put("protocol", protocol);
        map.put("address", address);
        return map;
    }

    @Bean
    public NettyServer nettyServer(XRequestHandler xRequestHandler, XConfig config) throws InterruptedException {

        XConfig.Provider provider = config.getProvider();
        if (provider == null) {
            log.debug("none start server");
            return null;
        }
        NettyServer nettyServer = new NettyServer(xRequestHandler, provider.getPort());
        new Thread(() -> {
            try {
                nettyServer.start();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).start();
        return nettyServer;
    }

    @Bean
    public LoadBalance loadBalance(XConfig config) {

        XConfig.Consumer consumer = config.getConsumer();
        if (consumer == null) {
            log.debug("none loadBalance");
            return null;
        }
        String strategy = consumer.getLoadBalance();
        if (strategy == null) {
            strategy = "random";
        }

        LoadBalance loadBalance = null;
        switch (strategy) {
            case "random":
                loadBalance = new RandomLoadBalance();
                break;
        }
        return loadBalance;
    }

    @Bean
    public Cluster cluster(Registry registry, LoadBalance loadBalance, ChannelProvider provider, XConfig xConfig) {

        XConfig.Consumer consumer = xConfig.getConsumer();
        if (consumer == null) {
            log.debug("none cluster");
            return null;
        }
        String policy = consumer.getClusterFaultTolerancePolicy();
        if (Objects.equals(policy, "")) {
            policy = "failFaster";
        }

        NettyClient nettyClient = new NettyClient(registry, loadBalance, provider);
        Cluster cluster = null;
        switch (policy) {
            case "failFaster":
                cluster = new FailFastCluster(nettyClient);
                break;
            case "failOver":
                cluster = new FailOverCluster();
                break;
        }
        return cluster;
    }

}
